package com.filetransfer.api.upload;
import com.filetransfer.api.generated.invoker.ApiException;
import com.filetransfer.api.generated.models.ChunkInfo;
import com.filetransfer.utils.StringFormat;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Objects;

import cn.hutool.core.lang.func.Func0;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class FileUpload {
    private final int chunkSize;
    public FileUpload(int chunkSize) {
        this.chunkSize = chunkSize;
    }
    public interface ChunkProgressListener {
        void onProgress(ProgressInfo progressInfo);
    }
    // 返回md5
    public String uploadFile(String fileName, Func0<InputStream> getFile, ChunkProgressListener onProgress) {
        var helper = new FileChunkPacker(fileName, getFile, chunkSize);
        var fullFileMd5 = helper.getFileMd5();
        if (fullFileMd5 == null || fullFileMd5.isEmpty()) {
            throw new IllegalArgumentException("文件 MD5 计算失败，MD5 值为空！");
        }
        log.info("文件名: {}, 文件大小: {}, 文件 MD5: {}", helper.getFileName(), StringFormat.formatSize(helper.getTotalSize()), fullFileMd5);
        var uploader = new FileChunkUploader(helper.getFileName(), fullFileMd5, helper.getTotalSize());
        var status = new ProgressStatus(helper.getChunkCount(), helper.getTotalSize());
        try (var iterator = helper.prepare()) {
            // 已经上传的分片信息
            List<ChunkInfo> chunks;
            if (uploader.getFileApi().checkUploadExists(fullFileMd5)) {
                log.info("文件记录已存在，准备断点续传: {}", fileName);
                // 获取已上传的分片信息
                chunks = uploader.getFileApi().getAllChunks(fullFileMd5);
            } else {
                chunks = null;
                log.info("无记录，准备上传: {}", fileName);
                uploader.start();
            }
            iterator.forEachRemaining(chunk -> {
                status.setWorkingChunkBytesWritten(0);
                status.workingChunkSize = chunk.chunkData().length;
                try {
                    if (chunks != null) {// 有已上传的分片
                        // 查找当前分片的上传记录
                        var chunkInfoOptional = chunks.stream().filter(c -> {
                            assert c.getChunkIndex() != null;
                            return c.getChunkIndex().equals(chunk.chunkIndex());
                        }).findFirst();
                        if (chunkInfoOptional.isPresent()) {
                            var chunkInfo = chunkInfoOptional.get();
                            // 检查 MD5 是否与历史记录一致
                            var historyChunkMD5 = chunkInfo.getChunkMD5();
                            if (Objects.equals(historyChunkMD5, chunk.chunkMD5())) {
                                // MD5 一致，跳过上传
                                log.info("分片 [{}] 已上传，跳过", chunk.chunkIndex());
                                status.totalBytesWritten += chunk.chunkData().length;
                                status.workingChunkBytesWritten = chunk.chunkData().length;
                                onProgress.onProgress(status.getProgressInfo());
                                return; // 跳过当前分片
                            } else {
                                // MD5 不一致，重新上传
                                log.info("分片 [{}] 已上传，但 MD5 不匹配，重新上传", chunk.chunkIndex());
                            }
                        }
                    }
                    log.info("开始上传分片: {}, 起始位置：{}，大小：{}， 分片 MD5：{}", chunk.chunkIndex(), StringFormat.formatSize(chunk.chunkOffset()), StringFormat.formatSize(chunk.chunkData().length), chunk.chunkMD5());
                    var success = uploader.uploadFileChunk(chunk, (bytesWritten, contentLength) -> {
                        status.workingChunkBytesWritten = bytesWritten;
                        status.workingChunkSize = contentLength;
                        onProgress.onProgress(status.getProgressInfo());
                    });
                    if (!success) {
                        log.error("上传失败，跳过分片: {} ", chunk.chunkIndex());
                    } else {
                        status.totalBytesWritten += chunk.chunkData().length;
                        status.workingChunkBytesWritten = 0;
                        log.info("分片 [{}] 上传成功", chunk.chunkIndex());
                    }
                } finally {
                    status.currentChunkIndex++;
                    onProgress.onProgress(status.getProgressInfo());
                }
            });
            log.info("文件上传完成: {}", fileName);
            return uploader.complete();
        } catch (IOException e) {
            uploader.cancel();
            log.error("爆了");
            throw new RuntimeException(e);
        } catch (ApiException e) {
            log.error("爆了");
            uploader.cancel();
            throw new RuntimeException(e);
        }
    }
    public String uploadFile(File file, ChunkProgressListener onProgress) {
        if (!file.exists() || !file.isFile()) {
            throw new IllegalArgumentException("无效的文件路径: " + file.getAbsolutePath());
        }
        return this.uploadFile(file.getName(), () -> {
            try {
                return new FileInputStream(file);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }, onProgress);
    }
}